(一)初识 Vue

  学习 Vue 之前,先简单了解前端是如何发展的。

前端发展历程

  前端历史简述如下:

  • 静态页面:最初的网页以 HTML 为主,是纯静态的网页。网页是只读的,信息流只能从服务端到客户端单向流通。此时开发人员只需关心页面的样式( CSS )和内容
  • 动态页面:当 JavaScript 出来以后,开发人员通过 JS 操作页面的 DOM 元素使页面动态化;
  • 异步交互:当 Ajax 技术崭露头角,开发人员不用刷新整个页面就能局部更新页面的数据并渲染结果。通过 Ajax 与后端交互,极大的提高了用户体验。

  此时的开发人员既要编写 CSS 样式,又要通过 JS 操作 DOM 元素来实现页面动态效果,还要通过 Ajax 来与后端交互。这时出现了一个非常流行的简易 JS 框架—— Jquery 。

  直到一个基于事件循环的异步 I/O 框架 Node.js 出现,让 JS 迈向了后端开发;但更为重要的是,Node.js 与其包管理工具 NPM 并驾齐驱,共同构建了一个庞大的生态系统,使得一大批前端框架涌现出来:

前端框架 架构 发布时间 GitHub Star
Knockout MVVM 2010.7 8k
Backbone MVP 2010.10 26k
Angular MVC->MVVM 2010.10 24k
Ember MVVM 2011.12 18k
Meteor MVC 2012.1 38k
Vue MVVM 2014.7 146k

:简单来说,Node 就是运行在服务端的 Javascript,而 npm( Node Package Manager)是 node 包管理和分发工具。

  从上表表可以看出:大部分前端框架的架构为 MVVM 。那什么是 MVVM 呢?

  MVVMModel–view–viewmodel)是一种软件架构模式

  • M:Model模型,包括数据和一些基本操作;
  • V:View视图,页面渲染结果;
  • VM:View-Model,模型与视图间的双向操作(无需开发人员干涉)

  MVVM 有助于将图形用户界面的开发与业务逻辑后端逻辑(数据模型)的开发分离开来,这是通过置标语言或 GUI 代码实现的。MVVM 的视图模型是一个值转换器,[1] 这意味着视图模型负责从模型中暴露(转换)数据对象),以便轻松管理和呈现对象。在这方面,视图模型比视图做得更多,并且处理大部分视图的显示逻辑。[1] 视图模型可以实现中介者模式,组织对视图所支持的用例集的后端逻辑的访问。

  在 MVVM 之前,开发人员通过 MVC 架构,使用 Ajax 从后端的 Controller 获取需要的数据 Model ,然后通过 DOM 操作将 Model 渲染到 View 中。当用户操作 View 时,又需要通过 DOM 获取 View 中的数据同步到 Model 中。

  如今看来,这种操作是比较麻烦的。
  当 MVVM 出现后,其 VM 将把麻烦的 DOM 操作完全封装起来,让开发人员不用再关心 Model 和 View 之间是如何影响的,使得该过程自动化,即:

  • 只要 Model 发生了改变,View 自动就会改变;
  • 当用户修改了 View,Model 的数据也自动地随之改变

  MVVM 将开发人员从繁琐的 DOM 操作中解放出来,而将关注点放在如何操作Model 上,这无疑大大提高了开发效率。
  Vue 作为 MVVM 中最火的前端框架,又怎能不学习呢?

什么是 Vue ?

  Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。

  Vue 的简单功能包括:

  • 声明式渲染
  • 条件与循环
  • 处理用户输入
  • 组件化应用构建

  本文一个极简的笔记便于记忆,若想了解 Vue 更具体的介绍(如按照)请参考其官网。

Vue 实例

  可以分为:

  • Vue 实例的创建:

    1
    2
    3
    const vm = new Vue({
    // 选项
    })
  • 数据绑定:Vue 实例对象属性(Model)可以与 DOM (DOM 渲染后为视图(View)进行绑定

  • 实例的生命周期钩子:每个 Vue 实例在被创建时都要经过一系列的初始化过程。如设置数据监听、编译模板、将实例挂载到 DOM 并在数据变化时更新 DOM 等。这个过程中也会运行一些叫做生命周期钩子的函数,这给了用户在不同阶段添加自己的代码的机会。这些钩子包括:
    • created:可以用来在一个实例被创建之后执行代码:
    • mounted:挂载时执行代码
    • updated:更新时执行代码
    • destroyed:销毁时执行代码

数据绑定

  Vue 实例对象属性(Model)可以与 DOM (DOM渲染后为视图(View))进行绑定。
  按笔者的理解,绑定分为 2 种:

  • 单向数据绑定:当属性变化时视图也跟着变化,但视图变化时属性不会变化
  • 双向数据绑定:当属性变化时视图也跟着变化,且视图变化时属性也变化

模板语法(单向数据绑定)

  模板语法允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据,使用双大括号来将插值插入,插值支持的种类如下:

  • 文本
  • 原始 HTML
  • JavaScript 表达式
  • 指令

文本

Mustache (胡须)语法(常用)

  数据绑定最常见的形式就是使用 Mustache 语法 (双大括号) 的文本插值:

1
<span>{{ msg }}</span>

  Mustache 标签将会被替代为对应数据对象上msg属性的值。无论何时,绑定的数据对象上msg属性发生了改变,插值处的内容都会更新。

一次性插值

  若使用v-once指令可以执行一次性地插值,之后当数据改变时,插值内容不会更新:

1
<span v-once>这个将不会改变: {{ msg }}</span>

原我本真

  v-pre指令用于跳过这个元素和它子元素的编译过程,用于显示原本的 Mustache 语法:

1
2
3
4
<div id="app">
<p>{{msg}}</p>
<p v-pre>{{msg}}</p>
</div>

插值闪烁

  由于存在插值闪烁(网络较差显示页面时会直接显示双大括号和msg)问题。
  因此,对网络较差的情况,应给用户的页面直接显示空白。而使用v-cloak指令或v-text指令或v-html指令便可实现该效果。

隐形斗篷

  在 Vue 解析之前,v-cloak 指令绑定相关标签后,通过将其style设置为none来达到空白效果,在 Vue 解析之后,该v-cloak属性将自动去除:

1
2
3
4
5
6
7
8
9
10
11
12
<head>
<style>
[v-cloak] {
display: none;
}
</style>
</head>
<body>
<div id="app" v-cload>
{{msg}}
</div>
</body>

  这就好像通过一件斗篷把要展示的事物先遮住,触发某些动作时才揭开展示。

v-text 指令

  另一种方案是使用v-text指令来代替双大括号:

1
<span v-text="msg"></span>

  但若是 HTML 代码,此方式会直接输出文本,这时又可以使用v-html指令来代替它。

原始 HTML

  双大括号或v-text指令会将数据解释为普通文本,而非 HTML 代码。为了输出真正的 HTML,可以使用v-html指令:

1
<span v-html="<span style="color: red" 红色的HTML内容"></span>

JavaScript 表达式

  对于所有的数据绑定,Vue.js 都提供了完全的 JavaScript 表达式支持。如:

1
2
3
4
5
{{ number + 1 }}

{{ ok ? 'YES' : 'NO' }}

{{ message.split('').reverse().join('') }}

注意哦:每个绑定都只能包含单个表达式,而不可以写一组语句。

指令

  带有v-前缀的指令,其后以冒号:表示(动态)参数,之后以=代表值,值预期是单个 JavaScript 表达式,比如v-on:click="doSomething";

注意哦:有两个常用的指令可以进行缩写,如v-bind缩写为:v-on缩写为@,后面会介绍它们。

v-model 双向数据绑定

  前面模版语法的插值都是单向绑定,即 Vue 实例中的data模型数据可以影响视图的渲染,但视图的改变不会影响到data模型的数据。
  而 Vue 的v-model指令则提供了双向数据绑定功能,使得视图和模型之间可以相互影响。
  既然是双向绑定,则视图必须可以修改,这就限定了视图的元素类型。
  因此,v-model可作用如下的类型元素:

  • 文本:text(常用)、passwordemail
  • 多行文本:textarea
  • 选择框:select
  • 单选按钮:radio
  • 复选框:checkbox
  • Vue 的自定义组件:components

  除了最后一项,其他基本都是表单的元素(部分未列出)。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<div id="app" style="text-align: center">
<form>
<!--文本-->
<input type="text" v-model="single"></br>
{{single}}</br>

<!--多行文本-->
<textarea v-model="s"></textarea></br>
{{s}}</br>

<!--复选框-->
<div>请选择你的爱好:{{hobby}}</div>
<input type="checkbox" v-model="hobby" value="唱歌">唱歌
<input type="checkbox" v-model="hobby" value="跳舞">跳舞
<input type="checkbox" v-model="hobby" value="打篮球">打篮球</br></br>

<!--单选按钮-->
<div>请选择你的性别:{{sex}}</div>
<input type="radio" v-model="sex" value="男">
<input type="radio" v-model="sex" value="女"></br></br>

<!--选择框-->
<div>你选择的地区:{{cities}}</div>
<select v-model="cities">
<option disabled value="">请选择地区</option>
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
</select>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
single: "这是一行神奇的文本",
s: "这是多行神奇的文本,这是多行神奇的文本,这是多行神奇的文本,这是多行神奇的文本",
hobby: [],
sex: '',
cities: ''
}
})
</script>

  页面结果如下图:


注意哦v-model会忽略所有表单元素的valuecheckedselected特性的初始值而总是将 Vue 实例的数据作为数据来源,因此你最好在data选项中声明初始值。

原理

  v-model其实是一个语法糖,它本质包含两个操作:

  • v-bind绑定一个value属性
  • v-on绑定当前元素的事件(如input
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<!-- 下面 2 行代码作用效果相同 -->
<!--<input type="text" v-model="msg">-->

<input type="text" :value="msg" @input="msg = $event.target.value">
{{msg}}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
msg: ""
},
})
</script>

值绑定

  对于单选按钮,复选框及选择框的选项,v-model绑定的值通常是静态字符串 (对于复选框也可以是布尔值),但是有时我们可能想把值绑定到 Vue 实例的一个动态属性上,这时可以用v-bind实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<p>你的爱好是:{{hobbies}}</p>
<label v-for="hobby in originHobbies" :for="hobby">
<input type="checkbox" :value="hobby" :id="hobby" v-model="hobbies">{{hobby}}
</label>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
hobbies:[],
originHobbies: ['唱歌','跳舞','打篮球']
},
})
</script>

v-model 的修饰符

  使用v-model的修饰符有 3 个,它们具有不同的功能:

  • .lazy:在默认情况下,v-model在每次 input 事件触发后将输入框的值与数据进行同步。使用此修饰符转可以让数据在失去焦点或会车时才更新:

    1
    <input v-model.lazy="msg" >
  • .number:可以自动将用户的输入值转为数值类型:

    1
    <input v-model.number="age" type="number">
  • .trim:可以自动过滤用户输入的首尾空白字符:

    1
    <input v-model.trim="msg">

computed 计算属性

  模板内使用表达式很方便,但其设计初衷仅用于简单的运算,毕竟在模版中放入太多的逻辑会让其变的臃肿且难以维护,这时可用计算属性来代替表达式。
  计算属性在 Vue 实例中使用computed来表示,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="example">
<p>初始消息 {{message}}</p>
<p>计算属性反转初始消息: {{reversedMessage}}</p>
<!-- <p>计算属性反转初始消息: <span v-text="reversedMessage"></span></p>-->
<!-- <p>初始消息 <span v-text="message"></span></p>-->
<p>书的总价格为:{{totalPrice}}</p>
</div>
<script src="node_modules/vue/dist/vue.js"></script>
<script>
const vm = new Vue({
el: "#example",
data: {
message: "Hello",
books: [
{id: 60001, name: '人月神话', price: 58},
{id: 60002, name: 'Java 并发编程', price: 49},
{id: 60003, name: '深入理解 JVM', price: 68}
]
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split("").reverse().join("");
},
totalPrice: function () {
let result = 0;
for (let i = 0; i < this.books.length; i++) {
result += this.books[i].price;
}
return result;
}
}
})
</script>
</body>
</html>

  输出结果:

1
2
3
4
5
初始消息 Hello

计算属性反转初始消息: olleH

书的总价格为:175

注意哦:与普通的方法相比,计算属性可以将结果进行缓存,提高性能。

watch 监控

  在v-model执行中,可以对数据进行双向绑定,这种数据绑定是 Vue 自动完成的。
  既然可以自动完成,那 Vue 中有没有一种机制可以让我们手动去监控数据呢?
  当然有呀,Vue 实例中的watch属性就实现了该功能。

浅监控

  watch可以让我们监控一个值的变化,从而做出相应的反应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div id="app">
<input type="text" v-model="message">
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
message: '',
},
watch: {
message(newValue, oldValue) {
alert("新值:" + value + ";旧值:" + old);
},
},
})
</script>

  上面我们使用watch属性监控了message这个变量,将其和一个函数绑定起来(函数名和变量相同),这个函数的第一个参数代表新值,第二个参数代表旧值。
  当我们将message这个变量绑定到文本框中,只要其中的内容发生改变,就会出现一个弹窗,输出新值和旧值。

深度监控

  但是若监控的是一个对象,则必须使用深度监控才能监控到该对象的属性变化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<div id="app">
<input type="text" v-model="person.name"/>
<input type="text" v-model="person.age"/>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
person: {
name: 'wangwu',
age: '18',
}
},
watch: {
person: {
// 开启深度监控才可以监控到对象的属性变化
deep: true,
// 定义监控后的处理方法
handler(val) {
alert(val.name + ":" + val.age);
}
}
},
})
</script>

  通过使用watch选项允许我们执行异步操作 (访问一个 API ),限制我们执行该操作的频率,可以在我们得到最终结果前,设置中间状态。

v-if 条件渲染

  条件渲染有点于类似于常见的if else if else条件语句,但 Vue 中不同的是条件渲染的指令都需要作为一个元素的属性才能使用。
  用于条件性地渲染一块内容的指令包括:

  • v-if
  • v-else-if
  • v-else
  • v-show

v-if 指令

  示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div id="tl">
<button @click="ok = !ok">点击让ok切换真假</button>
<div v-if="ok">
ok为true才能看到此消息
</div>
<div v-else>
否则显示此消息
</div></br>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const tl = new Vue({
el: "#tl",
data: {
ok: true,
}
})
</script>

注意哦v-else元素必须紧跟v-ifv-else-if元素其后,否则它将不被识别。

<template> 判断多个元素

  若想切换多个元素呢?此时可以把一个<template>元素当做不可见的包裹元素,并在上面使用v-if。最终的渲染结果将不包含<template>元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="tl">
<template v-if="ok">
<h1>标题</h1>
<p>段落 1</p>
<p>段落 2</p>
</template>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const tl = new Vue({
el: "#tl",
data: {
ok: true,
}
})
</script>

注意哦:要想使用v-相关指令,必须将使用了该指令的标签声明为一个Vue实例,否则该指令无效,使用<template>元素时必须将其放在一个标签内。

v-show 指令

  另一个用于根据条件展示元素的选项是v-show指令。用法大致一样:

1
2
3
4
5
6
7
8
9
10
<h1 id="h" v-show="ok">Hello!</h1>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const tl = new Vue({
el: "#h",
data: {
ok: true,
}
})
</script>

注意哦v-show不支持<template>元素,也不支持v-else

v-if 对比 v-show

  v-if有如下特点:

  • “真正”的条件渲染,会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。
  • 惰性的:若在初始渲染时条件为假,则什么也不做,直到条件第一次变为真时,才会开始渲染条件块。
  • 被渲染的元素始终会保留在 DOM 中

  v-show 有如下特点:

  • 不管初始条件是什么,元素总是会被渲染。
  • 被渲染的元素只是简单地切换元素的 CSS 属性 display

  一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,若需要非常频繁地切换,则使用 v-show 较好;若在运行时条件很少改变,则使用 v-if 较好。

v-for 列表渲染(遍历)

  在 Vue 中可以通过v-for指令来渲染(遍历)一个列表,其可以渲染:

  • 数组(常用)
  • 对象
  • 数值

数组渲染(常用)

  v-for指令能基于一个数组来渲染一个列表。该指令需要遵循item in items形式的特殊语法,其中 items 是源数据数组,而 item 则是被迭代的数组元素的别名:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div>
<ul id="demo">
<li v-for="pro in products">
{{pro}}
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el:"#demo",
data:{
products:[
"苹果", "葡萄"
]
}
})
</script>

  页面输出结果:

  • 苹果
  • 葡萄

  在遍历的过程中,若想知道数组的索引下标,可以指定第二个参数:

1
2
3
4
5
<ul id="demo">
<li v-for="(p,index) in products">
{{index}} : {{p}}
</li>
</ul>

  页面输出结果:

  • 0 : 苹果
  • 1 : 葡萄

数组中的响应式方法

  数组中并不是所有的方法都是响应式的,来看一个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="changeElement">切换数组的元素</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
letters: ['a', 'b', 'c', 'd']
},
methods: {
changeElement() {
this.letters[0] = 'z';
}
}
});
</script>

  当我们点击按钮后,会发现 0 索引的数组元素并没有响应式地切换。别担心,后面我们会解决它。
  先来看看数组中的哪些方法是响应式的,如下:

  • push():往数组末尾添加元素
  • pop():出栈,即移除数组末尾的元素
  • shift:删除数组头部元素
  • unshift():往数组头部添加元素,可以同时添加多个
  • sort():排序所有元素
  • reverse():元素反转
  • splice(start: number, deleteCount: number, ...items: T[]):根据不同参数可以删除元素、替换元素、插入元素:
    • splice(start: number, deleteCount: number):删除指定个数的元素,第一个参数代表索引开始位置,第二个参数代表删除元素的个数(若仅指定索引参数,则删除该索引后的所有元素)
    • splice(start: number, deleteCount: number, ...items: T[]):替换元素,第一个参数代表索引开始位置,第二个参数代表删除元素的个数,第三个参数数组代表需要添加的元素。
    • splice(start: number, 0, ...items: T[]):插入元素,即删除 0 个元素,而在指定索引位置插入新的元素
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<div id="app">
<ul>
<li v-for="item in letters">{{item}}</li>
</ul>
<button @click="changeElement">切换数组的元素</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
letters: ['a', 'b', 'c', 'd']
},
methods: {
changeElement() {
// 往数组末尾添加元素
// this.letters.push("b");

// 出栈,即移除数组末尾的元素
// this.letters.pop();

// 删除数组头部元素
// this.letters.shift();

// 往数组头部添加元素,可以同时添加多个
// this.letters.unshift();

// 删除指定索引后的指定个数的元素
// this.letters.splice(1,2);
// 删除指定索引后所有元素
// this.letters.splice(1);

// 替换元素
// this.letters.splice(1,2,'f','g');

/**
* Vue 提供的 set 方法替换元素:
* 第一个参数:要修改的对象
* 第二个参数:索引值
* 第三个参数:替换后的元素
*/
// Vue.set(this.letters,1,'f');


// 插入元素
// this.letters.splice(1,0,'f','g');

//无效的方法
// this.letters[0] = 'z';
}
}
});
</script>

  我们可以通过splice方法来达到替换数组元素的效果,当然 Vue 中页提供了一个 set 方法来达到该效果。

对象渲染

  v-for指令不仅可以遍历数组,还可以遍历对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div>
<ul id="test">
<li v-for="value in person">
{{value}}
</li>
</ul>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm2 = new Vue({
el:"#test",
data:{
person:{
name:"王五",
age:18,
}
}
});
</script>

  页面输出结果:

  • 王五
  • 18

  在遍历的过程中,若想知道对象的键名,可以指定第二个参数:

1
2
3
4
5
<ul id="test">
<li v-for="(value,name) in person">
{{name}} : {{value}}
</li>
</ul>

  页面输出结果:

  • name : 王五
  • age : 18

  在遍历的过程中,若想知道对象的索引下标,还可以指定第三个参数:

1
2
3
4
5
<ul id="test">
<li v-for="(value,name,index) in person">
{{index}} : {{name}} : {{value}}
</li>
</ul>

  页面输出结果:

  • 0 : name : 王五
  • 1 : age : 18

简单数值

  v-for指令还可以遍历简单数值,如:

1
2
3
<li v-for="num in 5">
{{num}}
</li>

注意哦:官方推荐我们在使用v-for时,给对应的元素或组件上添加一个:key属性来高效的更新虚拟 DOM

v-bind: 增强

  前面所学指令主要作用是将值插入到模版的内容当中。
  但是,除了内容需要动态来展示外,某些属性我们也希望动态来绑定:

  • 如动态绑定a元素的href属性
  • 如动态绑定img元素的src属性

  此时可以使用v-bind指令来增强 HTML 的原有属性。

快速入门

  考虑一个情况,我们将 HTML 的某些标签通过其class属性定义了其样式,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<style>
.redbox {
width: 160px;
height: 160px;
background-color: red;
}

.greenbox {
width: 160px;
height: 160px;
background-color: green;
}
</style>
<body>
<div class="redbox">
我是一个想变化的盒子
</div>
<div class="greenbox">
我是一个想变化的盒子
</div>
<div class="redbox">
我是一个想变化的盒子
</div>
<div class="greenbox">
我是一个想变化的盒子
</div>

  但现在这个盒子说:“我想换个颜色,换个心情”
  对于这个需求,你的第一想法应该是把class的内容改变一下,或许你会硬编码(直接改),或许你会通过函数来实现。
  这种想法没毛病,但 Vue 有更好的做法。
  你可能会想在 Vue 中通过模版语法来修改其class,但这样是行不通的,因为class本身并不属于 Vue,若你这么做:

1
2
3
4
5
6
7
8
9
10
11
12
<div id="app">
<div class="color">
我是一个想变化的盒子
</div>
</div>
...
const vm = new Vue({
el:"#app"
data:{
color:"greenbox"
}
})

  则页面将会报错:插值表达式不能使用在class属性上。
  那我们该怎么办呢?
  我们可以通过v-bind指令来增强class,使得 Vue 支持它:

1
2
3
<div v-bind:class="color">
我是一个想变化的盒子
</div>

注意哦v-bind指令有省略写法,所以可以这么写简化代码:

1
2
3
<div :class="color">
我是一个想变化的盒子
</div>

绑定 class

  前面说过,v-bind指令可以增强 HTML 中的class,我们可以传给v-bind:class 一个三元表达式,以动态地切换class

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div id="app">
<button @click="ischange = !ischange">切换盒子的颜色</button>
<div v-bind:class="ischange ? 'redbox' : 'greenbox'">
我是一个想变化的盒子
</div>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#app",
data: {
ischange: true,
}
})
</script>

对象语法

  我们也可把一个对象传给v-bind:class,以应用一个class列表:

1
2
3
4
5
6
7
8
9
10
11
<div v-bind:class="color.red">
我是一个红色的盒子
</div>
...
data: {
color:{
red:"redbox",
green:"green",
},

}

数组语法

  我们还可把一个数组传给v-bind:class,以应用一个class列表:

1
2
3
4
5
6
<div v-bind:class="[activeClass, errorClass]"></div>
...
data: {
activeClass: 'active',
errorClass: 'text-danger'
}

  渲染结果:

1
<div class="active text-danger"></div>

绑定 style

  v-bind指令不仅可以增强 HTML 中的class,还可以增强style

对象语法

  v-bind:style的对象语法十分直观——看着非常像 CSS,但其实是一个 JavaScript 对象。
  CSS 属性名可以用驼峰式(camelCase)或短横线分隔(kebab-case,记得用引号括起来)来命名:

1
2
3
4
5
6
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
...
data: {
activeColor: 'red',
fontSize: 30
}

  直接绑定到一个样式对象通常更好,这会让模板更清晰:

1
2
3
4
5
6
7
8
<div v-bind:style="styleObject"></div>
...
data: {
styleObject: {
color: 'red',
fontSize: '13px'
}
}

数组语法

  v-bind:style的数组语法可以将多个样式对象应用到同一个元素上:

1
<div v-bind:style="[baseStyles, overridingStyles]"></div>

v-on: 事件处理

监听事件

  可以用 v-on 指令监听 DOM 事件,并在触发时运行一些 JavaScript 代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="test">
<button v-on:click="counter += 1">点我下面数字加1</button>
<p>按钮被点击了{{counter}}次</p>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el:"#test",
data:{
counter:0,
}
})
</script>

事件处理方法

  许多事件得处理逻辑比较复杂,所以直接把 JavaScript 代码写在v-on指令中是不可行的。这时,需要先在 Vue 实例的methods属性中定义一个函数方法,然后传递给v-on接收:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<div id="demo">
<button v-on:click="helloWorld">世界你好</button>
</div>

<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script>
const vm = new Vue({
el: "#demo",
methods: {
helloWorld: function () {
alert("Hello World");
}
}
})
</script>

  有时也需要在内联语句处理器中访问原始的 DOM 事件。可以用特殊变量$event把它传入方法:

1
2
3
4
5
6
7
8
9
10
11
12
<button v-on:click="warn('访问原生事件对象', $event)">
Submit
</button>

...
methods: {
warn: function (message, event) {
// 现在我们可以访问原生事件对象
if (event) event.preventDefault()
alert(message)
}
}

事件修饰符

  在事件处理程序中调用event.preventDefault()event.stopPropagation()是非常常见的需求。
  我们当然可以在方法中轻松实现这点,但更好的方式是:让方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节
  Vue.js 为v-on用事件修饰符实现了该效果,修饰符是由点开头的指令后缀来表示的,包括:

  • .stop
  • .prevent
  • .capture
  • .self
  • .once
  • .passive

  具体的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- 阻止单击事件继续传播 -->
<a v-on:click.stop="doThis"></a>

<!-- 提交事件不再重载页面 -->
<form v-on:submit.prevent="onSubmit"></form>

<!-- 修饰符可以串联 -->
<a v-on:click.stop.prevent="doThat"></a>

<!-- 只有修饰符 -->
<form v-on:submit.prevent></form>

<!-- 添加事件监听器时使用事件捕获模式 -->
<!-- 即元素自身触发的事件先在此处理,然后才交由内部元素进行处理 -->
<div v-on:click.capture="doThis">...</div>

<!-- 只当在 event.target 是当前元素自身时触发处理函数 -->
<!-- 即事件不是从内部元素触发的 -->
<div v-on:click.self="doThat">...</div>

<!-- 点击事件将只会触发一次 -->
<a v-on:click.once="doThis"></a>

<!-- 滚动事件的默认行为 (即滚动行为) 将会立即触发 -->
<!-- 而不会等待 `onScroll` 完成 -->
<!-- 这其中包含 `event.preventDefault()` 的情况 -->
<div v-on:scroll.passive="onScroll">...</div>

注意哦使用修饰符时,顺序很重要。如v-on:click.prevent.self会阻止所有的点击,而v-on:click.self.prevent只会阻止对元素自身的点击。

  更多的修饰符(如按键鼠标修饰符)见 Vue 官网

filters 过滤器

  过滤器可被用于一些常见的文本格式化,其可以用在两个地方:

  • 双花括号插值中
  • v-bind 表达式中
1
2
3
4
5
<!-- 在双花括号中 -->
{{ message | capitalize }}

<!-- 在 `v-bind` 中 -->
<div v-bind:id="rawId | formatId"></div>

  过滤器可以串联(形成过滤链):

1
{{ message | filterA | filterB }}

  过滤器是 JavaScript 函数,因此可以接收参数(不写括号时默认接收|前的参数):

1
{{ message | filterA('arg1', arg2) }}

  过滤器可以将后端传递的日期数值进行转化:

1
2
3
4
5
6
7
8
9
10
11
<td>{{p.createTime | dateFilter}}</td>

...
// 注意哦:需要引入 moment 库
filters: {
// 日期过滤
dateFilter(date) {
return moment(date).format("YYYY-MM-DD HH:mm:ss");
}
}
...

参考

0%